package com.mavenq.fly.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.util.CollectionUtils;
import org.apache.commons.lang3.StringEscapeUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.Statement;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;

//@Intercepts({
//        @Signature(type = Statement.class, method = "update", args = {Statement.class}),
//        @Signature(type = Statement.class, method = "batch", args = {Statement.class})
//})
@Intercepts({
        @Signature(
                type = StatementHandler.class,          // 拦截 Executor（MyBatis 执行器）
                method = "update",              // 拦截 update 方法（处理 INSERT/UPDATE/DELETE）
                args = {Statement.class}  // 方法参数
        )
})
@Slf4j
public class MybaitisIntercepor implements Interceptor {

    private static final String QUESTION_MARK_REPLACEMENT = "#ESC_QTEMP_R#";
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        if (!(method.getName().equals("update") || method.getName().equals("batch"))) {
            log.info("sql的method非update与batch ...");
            return invocation.proceed();
        }
        Object target = realTarget(invocation.getTarget());
        log.info("get real target method:{}", target.getClass().getName());

        StatementHandler statementHandler = (StatementHandler) target;
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, SystemMetaObject.NULL_META_OBJECT.getReflectorFactory());

        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

        Statement statement = (Statement) invocation.getArgs()[0];
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        log.info("originSql:{}", sql);
        Connection connection = statement.getConnection();

        String finalSql = getFinalSql(mappedStatement.getConfiguration(), boundSql);
        log.info("finalSql:{}", finalSql);
        return invocation.proceed();
    }

    private String getFinalSql(Configuration configuration, BoundSql boundSql) {
        // 获取参数
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // sql语句中多个空格都用一个空格代替
        String sql =  boundSql.getSql().replaceAll("[\\s]+", " ")
                .replaceAll("\\?", QUESTION_MARK_REPLACEMENT);
        if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {
            // 获取类型处理器注册器，类型处理器的功能是进行java类型和数据库类型的转换
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            // 如果根据parameterObject.getClass() 可以找到对应的类型，则替换
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst(QUESTION_MARK_REPLACEMENT, Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst(QUESTION_MARK_REPLACEMENT, Matcher.quoteReplacement(getParameterValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst(QUESTION_MARK_REPLACEMENT, Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        sql = sql.replaceFirst(QUESTION_MARK_REPLACEMENT, getParameterValue("缺失"));
                    }
                }
            }
        }
        return sql.replaceAll(QUESTION_MARK_REPLACEMENT, "?");
    }

    private String getParameterValue(Object obj) {
        if (obj == null) {
            return "null";
        }
        String value;

        if (obj instanceof String) {
            String objStr = (String) obj;
            value = "'" + escapeSql(objStr) + "'";
        } else if (obj instanceof Date) {
            Date date = (Date) obj;
            value = "'" + DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss") + "'";
        } else {
            value = obj.toString();
        }
        return value;
    }


    public String escapeSql(String input) {
        if (input == null) return "";
        return input
                .replace("'", "''")  // 单引号转义为两个单引号（SQL标准）
                .replace("\\", "\\\\")  // 反斜杠转义
                .replace(";", "")  // 移除分号
                .replace("--", "") // 移除SQL注释
                .replace("\"", "\\\"");  // 双引号转义
    }

    public static <T> T realTarget(Object target) {
        if (Proxy.isProxyClass(target.getClass())) {
            MetaObject metaObject = SystemMetaObject.forObject(target);
            return realTarget(metaObject.getValue("h.target"));
        }
        return (T) target;
    }
}
